using System;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class TwoImages
    {
    }

    /// <summary>
    /// LineDetector is an algorithm to find the lines in an image
    /// This needs a gray image two work
    /// </summary>
    public class LineDetector : BaseImageFilter
    {
        //The Convolution operator used as part of the process.
        //Convolution convolution;
        //the width and height of the output image

        protected int d_w;
        protected int d_h;
        protected int[,] dest_1d;

        //  //Create a new version of twoimages to put the output images in
        protected TwoImages twoimages = new TwoImages();

        /// <summary>
        /// 
        /// </summary>
        protected OwnOptimizedConvolutionInt m_OwnOptimizedConvolution = new OwnOptimizedConvolutionInt();

        //The kernels to be used in the detection function
        protected int[,] ker4 = { { -1, -1, -1 }, { 2, 2, 2}, { -1, -1, -1} };
        protected int[,] ker2 = { { -1, 2, -1 }, { -1, 2, -1}, { -1, 2, -1 } };
        protected int[,] ker3 = { { -1, -1, 2 }, { -1, 2, -1 }, { -2, -1, -1 } };
        protected int[,] ker1 = { { -2, -1, -1 }, { -1, 2, -1 }, { -1, -1, 2 } };

        protected bool m_bChoice = false;
        protected int m_nThreshold = 100;
        protected int m_nKernels = 2;

        /// <summary>
        /// Initializes a new instance of the <see cref="LineDetector"/> class.
        /// </summary>
        public LineDetector()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LineDetector"/> class.
        /// </summary>
        /// <param name="_lineDetector">The _line detector.</param>
        internal LineDetector(LineDetector _lineDetector)
        {
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LineDetector"/> is choice.
        /// </summary>
        /// <value><c>true</c> if choice; otherwise, <c>false</c>.</value>
        public bool Choice
        {
            get { return m_bChoice; }
            set { m_bChoice = value; }
        }

        /// <summary>
        /// Gets or sets the threshold.
        /// </summary>
        /// <value>The threshold.</value>
        public int Threshold
        {
            get { return m_nThreshold; }
            set { m_nThreshold = value; }
        }

        /// <summary>
        /// Gets or sets the kernels.
        /// </summary>
        /// <value>The kernels.</value>
        public int Kernels
        {
            get { return m_nKernels; }
            set { m_nKernels = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;
            ApplyLineDetect(_bitmap, nWidth, nHeight, m_nKernels, m_nThreshold, m_bChoice, m_fScale, m_fOffset);
            return _bitmap;
        }

        /// <summary>
        /// Applies the line detector to the input image
        /// a) assume the image is grey level (hence RR=GG=BB)
        /// b) use value 0x000000ff to get the BB value
        /// c) apply the selected kernels to the image
        /// d) return the output image
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="width">width of the destination image in pixels</param>
        /// <param name="height">height of the destination image in pixels</param>
        /// <param name="kernels">which kernels are applied</param>
        /// <param name="thresh">The threshold limit for kernel detection</param>
        /// <param name="choice">Boolean which specifies whether to apply thresholding</param>
        /// <param name="scale">The scale value to apply to the output</param>
        /// <param name="offset">The offset to add to the output</param>
        /// <returns>
        /// The output array with the line edges and the colored edge image
        /// </returns>
        public TwoImages ApplyLineDetect(UnsafeBitmap _bitmap, int width, int height,
                           int kernels, int thresh, bool choice,
                           float scale, float offset)
        {

            d_w = width;
            d_h = height;
            dest_1d = new int[d_w, d_h];
            int[,] label_1d = new int[d_w, d_h];
            double[,] tmp_1d = new double[d_w, d_h];
            double[,] tmp2_1d = new double[d_w, d_h];
            double[,] src_2d = new double[d_w, d_h];

            UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();
            //Initialise the destination and label array
            int x = 0;
            int y = 0;
            for (x = 0; x < d_w; x++)
            {
                for (y = 0; y < d_h; y++)
                {
                    tmp2_1d[x, y] = -1000.0;
                    label_1d[x, y] = Definitions.Black.ToArgb(); //0xff000000;
                }
            }

            //Change the source array into 2d double array for use with 
            //convolution operator.
            for (int i = 0; i < d_w; i++)
            {
                for (int j = 0; j < d_h; j++)
                {
                    //if (i==32 && j==100) System.out.println("Convolve bug: "+i+"+"+(j*d_w)); /// 26432 264 w 268 h
                    _bitmap.SetPixel(i,j,Color.FromArgb(_bitmap.GetPixel(i, j).ToArgb() & 0x000000ff)); //src_2d[i, j] = (double)(src_1d[i,j] & 0x000000ff);

                    //if (i==32 && j==100) System.out.println("Convolve data 2d: "+src_2d[i,j]+" "+src_1d[i+(j*d_w)]);
                }
            }

            //Find if the first kernel needs to be applied
            if ((kernels & 1) == 1)
            {
                m_OwnOptimizedConvolution.Kernel = ker1;
                m_OwnOptimizedConvolution.Execute(bitmapCloned);
                for (x = 0;x < d_w; x++)
                {
                    for (y = 0; y < d_h; y++)
                    {
                        if (tmp_1d[x,y] > tmp2_1d[x,y])
                        {
                            tmp2_1d[x,y] = tmp_1d[x,y];
                            label_1d[x,y] = Definitions.Yellow.ToArgb(); //0xffffff00;
                        }
                    }
                }
            }

            if ((kernels & 2) == 2)
            {
                m_OwnOptimizedConvolution.Kernel = ker2;
                m_OwnOptimizedConvolution.Execute(bitmapCloned);
                for (x = 0; x < d_w; x++)
                {
                    for (y = 0; y < d_h; y++)
                    {
                        if (tmp_1d[x,y] > tmp2_1d[x,y])
                        {
                            tmp2_1d[x,y] = tmp_1d[x,y];
                            label_1d[x,y] = Definitions.Red.ToArgb(); //0xffff0000;
                        }
                    }
                }
            }

            if ((kernels & 4) == 4)
            {
                m_OwnOptimizedConvolution.Kernel = ker3;
                m_OwnOptimizedConvolution.Execute(bitmapCloned);
                for (x = 0; x < d_w; x++)
                {
                    for (y = 0; y < d_h; y++)
                    {
                        if (tmp_1d[x, y] > tmp2_1d[x, y])
                        {
                            tmp2_1d[x, y] = tmp_1d[x, y];
                            label_1d[x, y] = Definitions.Green.ToArgb(); //0xff00ff00;
                        }
                    }
                }
            }

            if ((kernels & 8) == 8)
            {
                m_OwnOptimizedConvolution.Kernel = ker4;
                m_OwnOptimizedConvolution.Execute(bitmapCloned);
                     for (x = 0; x < d_w; x++)
                {
                    for (y = 0; y < d_h; y++)
                    {
                        //if (i==26432) System.out.println("Convolve bug tmp: "+tmp_1d[i]+" tmp2: "+tmp2_1d[i]);
                        if (tmp_1d[x,y] > tmp2_1d[x,y])
                        {
                            tmp2_1d[x,y] = tmp_1d[x,y];
                            label_1d[x,y] = Definitions.Blue.ToArgb(); //0xff0000ff;
                        }
                    }
                }
            }

            // convert to integer
            double tmp_rgb;
            int dest_rgb;
            for ( x = 0; x < d_w; x++)
            {
                for (y = 0; y < d_h; y++)
                {
                    //Find the absolute edge strength
                    tmp_rgb = tmp2_1d[x,y];

                    //Apply the threshold if necessary
                    if (choice)
                    {
                        if (tmp_rgb > thresh)
                        {
                            //No change in value
                        }
                        else
                        {
                            tmp_rgb = 0;
                            label_1d[x,y] = Definitions.Black.ToArgb(); //0xff000000;
                        }
                    }

                    //Apply scaling and offset
                    dest_rgb = (int)((scale * tmp_rgb) + offset);

                    //Clip values
                    if (dest_rgb < 0) { dest_rgb = 0; }
                    if (dest_rgb > 255) { dest_rgb = 255; }

                    //Output them
                    _bitmap.SetPixel(x,y, Color.FromArgb(Definitions.Black.ToArgb() | (dest_rgb + (dest_rgb << 16) + (dest_rgb << 8)))); //0xFF << 24 | (dest_rgb + (dest_rgb << 16)+ (dest_rgb << 8));
                }
            }


            //Set the output images of the return class
            //twoimages.image1 = dest_1d;
            //twoimages.image2 = label_1d;
            return twoimages;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new LineDetector(this);
        }
    }
}